package com.tcm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tcm.common.enums.CachePrefix;
import com.tcm.config.AppProperties;
import com.tcm.entity.*;
import com.tcm.entity.bo.UserCacheBo;
import com.tcm.entity.dto.CommentUpdateDto;
import com.tcm.entity.resp.PageR;
import com.tcm.entity.vo.CommentListResp;
import com.tcm.entity.vo.CommentListResult;
import com.tcm.exception.EntityInvalidException;
import com.tcm.service.*;
import com.tcm.mapper.CommentMapper;
import com.tcm.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Guqier
 * @description 针对表【ts_comment(题目评论表)】的数据库操作Service实现
 * @createDate 2023-09-09 20:32:29
 */
@Service
@RequiredArgsConstructor
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment>
        implements CommentService {

    private final RedisUtil redisUtil;

    private final CommentLikeService commentLikeService;

    private final UserService userService;

    private final ImageService imageService;

    private final SubjectItemService subjectItemService;

    private final ClozeItemService clozeItemService;

    private final NotificationService notificationService;

    /**
     * 添加评论
     * @param dto        评论数据Dto
     * @param currUserId 当前用户ID
     * @return 保存结果，true为成功
     */
    @Override
    @Transactional
    public boolean addComment(CommentUpdateDto dto, Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        Comment comment = new Comment();
        comment.setAccount(userCacheBo.getAccount());
        BeanUtils.copyProperties(dto, comment);
        comment.setCreateUser(currUserId);
        comment.setUpdateUser(currUserId);

        // 添加通知
        if (Objects.nonNull(dto.getParentId())) {
            Comment parentComment = this.getById(dto.getParentId());
            notificationService.addNotificationForStudent("评论有新的回复", "【" + userCacheBo.getNickname() + "】回复了你的评论【"
                            + parentComment.getContent()
                            + "】，回复内容：【"
                            + comment.getContent()
                            + "】",
                    AppProperties.NotificationType.IMPORTANT,
                    0, parentComment.getAccount(), currUserId);
        }

        return this.save(comment);
    }

    /**
     * 删除评论
     * @param id         评论ID
     * @param currUserId 当前登录用户ID
     * @return 删除结果，true为成功
     */
    @Override
    @Transactional
    public boolean removeCommentByStu(Long id, Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        Comment comment = this.getById(id);
        if (Objects.isNull(comment)) {
            throw new EntityInvalidException("评论不存在");
        }
        if (!comment.getAccount().equals(userCacheBo.getAccount())) {
            throw new EntityInvalidException("仅能删除自己的评论");
        }
        return this.removeById(id);
    }

    /**
     * 获取评论列表
     * @param itemId     题目ID
     * @param currUserId 当前登录用户ID
     * @return 评论列表
     */
    @Override
    public List<CommentListResp> getCommentList(Long itemId, Long currUserId) {
        // 获取学生信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        // 获取评论列表
        List<Comment> commentList = this.list(new LambdaQueryWrapper<Comment>().eq(Comment::getItemId, itemId).orderByDesc(Comment::getCreateTime));
        List<CommentListResp> resps = transferCommentToRespWithoutSubComments(commentList, userCacheBo.getAccount());
        List<CommentListResp> listResps = buildCommentTwoLevelStruct(resps);
        listResps.sort((o1, o2) -> o2.getLikeNum().compareTo(o1.getLikeNum()));
        return listResps;
//        return buildCommentTree(null, resps);
    }

    /**
     * 广度优先搜索查找子评论
     * @param parentId    父级评论ID
     * @param commentList 评论列表
     * @return 子评论列表
     */
    private List<CommentListResp> buildCommentTree(Long parentId, List<CommentListResp> commentList) {
        List<CommentListResp> subComments = new ArrayList<>();
        for (CommentListResp resp : commentList) {
            if (Objects.equals(resp.getParentId(), parentId)) {
                resp.setSubComments(buildCommentTree(resp.getId(), commentList));
                subComments.add(resp);
            }
        }
        return subComments;
    }

    /**
     * 构建两层评论结构
     * @param resps 评论集合
     * @return 结果
     */
    private List<CommentListResp> buildCommentTwoLevelStruct(List<CommentListResp> resps) {
        List<CommentListResp> rootComments = resps.stream().filter(commentListResp -> commentListResp.getRootId() == null).collect(Collectors.toList());
        for (CommentListResp resp : rootComments) {
            List<CommentListResp> subComments = resps
                    .stream()
                    .filter(commentListResp -> !rootComments.contains(commentListResp) && commentListResp.getRootId().equals(resp.getId()))
                    .sorted(Comparator.comparing(CommentListResp::getTime))
                    .collect(Collectors.toList());
            resp.setSubComments(subComments);
        }
        return rootComments;
    }

    /**
     * 忽略子评论将评论实体转换为评论响应类
     * @param commentList 评论列表
     * @return 评论响应类集合
     */
    private List<CommentListResp> transferCommentToRespWithoutSubComments(List<Comment> commentList, String account) {
        return commentList.stream().map(comment -> {
            CommentListResp resp = new CommentListResp();
            BeanUtils.copyProperties(comment, resp);
            User user = userService.getUserByAccount(comment.getAccount());
            if (Objects.nonNull(user.getAvatar())) {
                Image image = imageService.getImageById(user.getAvatar());
                resp.setAvatarUrl(image.getRemoteFileUrl());
            }
            resp.setTime(comment.getCreateTime());
            resp.setLikeNum(commentLikeService.getLikeNum(comment.getId()));
            resp.setNickname(user.getNickname());
            resp.setIsLike(commentLikeService.isLike(comment.getId(), account) ? 1 : 0);
            resp.setIsMine(comment.getAccount().equals(account));
            return resp;
        }).collect(Collectors.toList());
    }

    @Override
    public PageR<CommentListResult> getCommentListByPage(Integer page, Integer pageSize, String content, String account, List<Long> itemIds) {
        Page<Comment> commentPage = new Page<>(page, pageSize);
        commentPage.addOrder(OrderItem.desc("create_time"));

        // 构造查询条件
        LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<Comment>()
                .eq(StringUtils.hasText(account), Comment::getAccount, account)
                .like(StringUtils.hasText(content), Comment::getContent, content)
                .in(!CollectionUtils.isEmpty(itemIds), Comment::getItemId, itemIds);

        // 查询数据
        this.page(commentPage, queryWrapper);

        // 构造结果集
        PageR<CommentListResult> pageR = new PageR<>();
        BeanUtils.copyProperties(commentPage, pageR, "records");
        pageR.setRecords(commentPage.getRecords().stream().map(comment -> {
            User user = userService.getUserByAccount(comment.getAccount());
            CommentListResult result = new CommentListResult();
            BeanUtils.copyProperties(comment, result);
            result.setLikeNum(commentLikeService.getLikeNum(comment.getId()));
            result.setTime(comment.getCreateTime());
            result.setName(user.getName());
            Optional.ofNullable(user.getAvatar()).ifPresent(id -> result.setAvatarUrl(imageService.getImageById(id).getRemoteFileUrl()));
            if (comment.getItemType().equals(AppProperties.ITEM_TYPE_SUBJECT)) {
                SubjectItem subjectItem = subjectItemService.getSubjectItemById(comment.getItemId());
                Optional.ofNullable(subjectItem).ifPresent(item -> result.setItemContent(item.getContent()));
            } else {
                ClozeItem clozeItem = clozeItemService.getClozeItemById(comment.getItemId());
                Optional.ofNullable(clozeItem).ifPresent(item -> result.setItemContent(item.getContent()));
            }
            return result;
        }).collect(Collectors.toList()));
        return pageR;
    }

    @Override
    public boolean removeComment(Long id) {
        return this.removeById(id);
    }

}




